import React, { useRef, useEffect, useImperativeHandle, useState } from "react"
import cssStyle from './index.module.css'
import close from '@/assets/images/close.png'
import Form from '../form'
import arrow from '@/assets/images/arrow-gray.png'

const key = Symbol('key')

const App = React.forwardRef((props, ref) => {
    const length = useRef(0)
    const [sourceData, setSourceData] = useState([])
    const [options, setOptions] = useState([])
    const dom = useRef(null)
    const transferRight = useRef(null)
    const noneSpan = useRef(null)
    const count = useRef(0)
    const dropIndex = useRef(-1)
    const originalDomIndex = useRef(-1)

    const filterKey = val => {
        return val.map(item => {
            const _item = {}
            for (const k in item) {
                _item[k] = item[k]
            }
            return _item
        })
    }
    const dragLeft = (e, id) => {
        e.dataTransfer.setData('id', id)
    }
    const dragRight = (e, index) => {
        dom.current = e.target
        dom.current.style.opacity = 0.5
        originalDomIndex.current = index
    }
    const dragEnd = e => {
        const children = Array.from(transferRight.current.children).filter(item => {
            return item.nodeName === 'P'
        })
        children.forEach(item => {
            item.style = ''
        })
        if (!dom.current.dataset.exchange) {
            return
        }
        setNewOptions(parseInt(dom.current.dataset.exchange))
        setTimeout(() => {
            dom.current.removeAttribute('data-exchange')
            dom.current = null
            count.current = 0
            dropIndex.current = -1
            originalDomIndex.current = -1
        })
    }
    const setNewOptions = exchangeIndex => {
        dom.current.style.opacity = 1
        const item = options[originalDomIndex.current]
        const _item = {}
        for (const k in item) {
            _item[k] = item[k]
        }
        const prev = [...options]
        options.splice(originalDomIndex.current, 1)
        options.splice(exchangeIndex, 0, item)
        setOptions([...options])
        const next = [...options]
        props.onChange({
            type: 'move',
            item: _item,
            originalIndex: originalDomIndex.current,
            exchangeIndex,
            prev: filterKey(prev),
            next: filterKey(next)
        })
    }
    const drop = e => {
        e.preventDefault()
        removePlaceholder()
        if (!dom.current) {
            const id = e.dataTransfer.getData('id')
            if (!props.repeat) {
                const exist = options.some(item => {
                    return item.id == id
                })
                if (exist) {
                    return
                }
            }
            const current = sourceData.filter(item => {
                return item.id == id
            })[0]
            if (dropIndex.current === -1) {
                dropIndex.current = options.length
            }
            const prev = [...options]
            length.current += 1
            options.splice(dropIndex.current, 0, {
                ...current,
                [key]: current.id + '-' + length.current
            })
            setOptions([...options])
            const next = [...options]
            props.onChange({
                type: 'add',
                item: current,
                index: dropIndex.current,
                prev: filterKey(prev),
                next: filterKey(next)
            })
            count.current = 0
            dropIndex.current = -1
        }
    }
    const getCurrentOverNode = target => {
        let el = null
        if (transferRight.current.contains(target) && transferRight.current !== target && !target.dataset.placeholder) {
            el = target
            if (el.nodeName !== 'P') {
                while (el.parentNode) {
                    el = el.parentNode
                    if (el.nodeName === 'P') {
                        break
                    }
                }
            }
        }
        return el
    }
    const allowDrop = e => {
        e.preventDefault()
        const el = getCurrentOverNode(e.target)
        if (el) {
            const children = Array.from(transferRight.current.children).filter(item => {
                return item.nodeName === 'P' && !item.dataset.placeholder
            })
            const rect = el.getBoundingClientRect()
            const isHalf = rect.top + rect.height / 2 < e.pageY
            if (dom.current) {
                if (el === dom.current) {
                    return
                }
                const hoverIndex = parseInt(el.dataset.index)
                if (originalDomIndex.current > hoverIndex) {
                    if (!isHalf) {
                        dropIndex.current = hoverIndex
                    } else {
                        dropIndex.current = hoverIndex + 1
                    }
                } else if (originalDomIndex.current < hoverIndex) {
                    if (isHalf) {
                        dropIndex.current = hoverIndex
                    } else {
                        dropIndex.current = hoverIndex - 1
                    }
                }
                if (dropIndex.current === -1) return
                el.style.transition = 'transform .15s'
                dom.current.style.transition = 'transform .15s'
                if (originalDomIndex.current === dropIndex.current) {
                    children.forEach(item => {
                        item.style.transform = 'translateY(0)'
                    })
                } else {
                    if (isHalf) {
                        if (dropIndex.current > originalDomIndex.current) {
                            children.forEach((item, index) => {
                                if (index <= dropIndex.current && index > originalDomIndex.current) {
                                    item.style.transform = 'translateY(-40px)'
                                } else {
                                    item.style.transform = 'translateY(0)'
                                }
                            })
                        } else {
                            for (let i = dropIndex.current; i < originalDomIndex.current; i += 1) {
                                children[i].style.transform = 'translateY(40px)'
                            }
                            if (dropIndex.current !== parseInt(dom.current.dataset.exchange)) {
                                children[parseInt(dom.current.dataset.exchange)].style.transform = 'translateY(0)'

                            }
                        }
                        dom.current.style.transform = `translateY(${(dropIndex.current - originalDomIndex.current)*40}px)`
                    } else {
                        if (dropIndex.current < originalDomIndex.current) {
                            for (let i = dropIndex.current; i < originalDomIndex.current; i += 1) {
                                children[i].style.transform = 'translateY(40px)'
                            }
                        } else {
                            console.log(dropIndex.current, originalDomIndex.current)
                            for (let i = originalDomIndex.current + 1; i < dropIndex.current; i += 1) {
                                children[i].style.transform = 'translateY(-40px)'
                            }
                            for (let i = dropIndex.current; i <= children.length; i += 1) {
                                children[i].style.transform = 'translateY(0)'
                            }
                        }
                        dom.current.style.transform = `translateY(${(dropIndex.current - originalDomIndex.current)*40}px)`
                    }
                }
                dom.current.dataset.exchange = dropIndex.current
            } else {
                dropIndex.current = children.findIndex(item => {
                    return item === el
                })
                if (isHalf) {
                    dropIndex.current += 1
                }
                const placeholderDomIndex = Array.from(transferRight.current.children).filter(item => {
                    return item.nodeName === 'P'
                }).findIndex(item => {
                    return item.dataset.placeholder
                })
                if (placeholderDomIndex === dropIndex.current) {
                    return
                }
                if (isHalf) {
                    addPlaceholder(el.nextSibling)
                } else {
                    addPlaceholder(el)
                }
            }
        } else {
            dropIndex.current = -1
        }
    }
    const deleteItem = index => {
        const prev = [...options]
        const item = {}
        for (const k in options[index]) {
            item[k] = options[index][k]
        }
        options.splice(index, 1)
        setOptions([...options])
        const next = [...options]
        props.onChange({
            type: 'remove',
            item,
            index,
            prev: filterKey(prev),
            next: filterKey(next)
        })
    }
    const dragEnter = e => {
        if (!dom.current) {
            count.current += 1
            if (count.current % 2) {
                if (noneSpan.current) {
                    noneSpan.current.style.display = 'none'
                }
                addPlaceholder()
            }
        }
    }
    const dragLeave = e => {
        count.current += 1
        if (!(count.current % 2)) {
            if (noneSpan.current) {
                noneSpan.current.style.display = 'unset'
            }
            removePlaceholder()
            count.current = 0
        }
    }
    const addPlaceholder = el => {
        const children = Array.from(transferRight.current.children)
        let placeholderDom = children.filter(item => {
            return item.dataset.placeholder
        })[0]
        if (!placeholderDom) {
            placeholderDom = document.createElement('p')
            placeholderDom.setAttribute('data-placeholder', true)
            placeholderDom.innerHTML = '放这里'
        }
        transferRight.current.insertBefore(placeholderDom, el)
    }
    const removePlaceholder = () => {
        const children = Array.from(transferRight.current.children)
        const placeholderDom = children.filter(item => {
            return item.dataset.placeholder
        })[0]
        if (placeholderDom) {
            transferRight.current.removeChild(placeholderDom)
        }
    }
    const doubleClick = current => {
        if (props.checkBox) {
            return
        }
        if (!props.repeat) {
            const exist = options.some(item => {
                return item.id == current.id
            })
            if (exist) {
                return
            }
        }
        const prev = [...options]
        length.current += 1
        options.push({
            ...current,
            [key]: current.id + '-' + length.current
        })
        setOptions([...options])
        const next = [...options]
        props.onChange({
            type: 'add',
            item: current,
            index: next.length - 1,
            prev: filterKey(prev),
            next: filterKey(next)
        })
    }
    const toRight = () => {
        const arr = sourceData.filter(item => {
            return item.checked
        })
        const _arr = sourceData.filter(item => {
            return !item.checked
        })
        if (arr.length) {
            arr.forEach(item => {
                item.checked = false
            })
            setOptions([...options, ...arr])
            setSourceData(_arr)
        }
    }
    const toLeft = () => {
        const arr = options.filter(item => {
            return item.checked
        })
        const _arr = options.filter(item => {
            return !item.checked
        })
        if (arr.length) {
            arr.forEach(item => {
                item.checked = false
            })
            setSourceData([...sourceData, ...arr])
            setOptions(_arr)
        }
    }
    const checkboxChangeLeft = (val, index) => {
        sourceData[index].checked = val
        setSourceData([...sourceData])
    }
    const checkboxChangeRight = (val, index) => {
        options[index].checked = val
        setOptions([...options])
    }
    const selectLeft = index => {
        if (props.checkBox) {
            sourceData[index].checked = !sourceData[index].checked
            setSourceData([...sourceData])
        }
    }
    const selectRight = index => {
        if (props.checkBox) {
            options[index].checked = !options[index].checked
            setOptions([...options])
        }
    }

    useImperativeHandle(ref, () => ({
        getSelection: () => {
            return filterKey(options)
        }
    }))
    useEffect(() => {
        setSourceData(props.data)
    }, [props.data])
    useEffect(() => {
        const list = [...props.value]
        length.current = list.length
        list.forEach((item, index) => {
            item[key] = item.id + '-' + index
        })
        setOptions(list)
    }, [props.value])
    return (
        <div className={ cssStyle.transfer }>
            <div className={ cssStyle.transferLeft }>
                {
                    sourceData.length
                    ?
                    sourceData.map((item, index) => {
                        return (
                            <p key={ index } draggable={ props.checkBox ? false : true } onDragStart={ e => { dragLeft(e, item.id) } } onDoubleClick={ () => { doubleClick(item) } } onClick={ () => { selectLeft(index) } } style={{ cursor: props.checkBox ? 'pointer' : 'move' }}>
                                {
                                    props.checkBox
                                    ?
                                    <Form.checkbox checked={ item.checked } label={ item.label } onChange={ val => { checkboxChangeLeft(val, index) } } />
                                    :
                                    <span>{ item.label }</span>
                                }
                            </p>
                        )
                    })
                    :
                    <span>暂无数据</span>
                }
            </div>
            {
                props.checkBox && (
                    <div className={ cssStyle.transferCenter }>
                        <div onClick={ toLeft }>
                            <img src={ arrow } style={{ transform: 'rotate(90deg)' }} />
                        </div>
                        <div onClick={ toRight }>
                            <img src={ arrow } style={{ transform: 'rotate(-90deg)' }} />
                        </div>
                    </div>
                )
            }
            <div className={ cssStyle.transferRight } ref={ transferRight } onDrop={ drop } onDragEnter={ dragEnter } onDragLeave={ dragLeave } onDragOver={ allowDrop }>
                {
                    options.length
                    ?
                    options.map((item, index) => {
                        return (
                            <p key={ item[key] || index } data-index={ index } draggable={ props.checkBox ? false : true } onDragStart={ e => { dragRight(e, index) } } onDragEnd={ dragEnd } onClick={ () => { selectRight(index) } }>
                                {
                                    props.checkBox
                                    ?
                                    <Form.checkbox checked={ item.checked } label={ item.label } onChange={ val => { checkboxChangeRight(val, index) } } />
                                    :
                                    <>
                                        <span>{ item.label }</span>
                                        <img src={ close } onClick={ () => { deleteItem(index) } } />
                                    </>
                                }
                            </p>
                        )
                    })
                    :
                    <span ref={ noneSpan }>暂无数据</span>
                }
            </div>
        </div>
    )
})

export default App